#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>

#define COMMAND_SIZE 1024
#define FORMAT "[%s@%s %s]# "

#define MAXARGC 128
int g_argc = 0;
char* g_argv[MAXARGC];

// 环境变量表
#define MAX_ENVS 100
char *g_env[MAX_ENVS];
int g_envs = 0;

// last exit code 
int lastcode = 0;

const char* GetUserName()
{
    const char *name = getenv("USER");
    return name == NULL ? "None" : name;
}

const char *GetHostName()
{
    const char *hostname = getenv("HOSTNAME");
    return hostname == NULL ? "None" : hostname;
}

char cwd[1024];
char cwdenv[1024];
const char *GetPwd()
{
    //const char *pwd = getenv("PWD");
    const char *pwd = getcwd(cwd,sizeof(cwd));
    if(pwd != NULL)
    {
        snprintf(cwdenv,sizeof(cwdenv),"PWD=%s",cwd);
        putenv(cwdenv);
    }
    return pwd == NULL ? "None" : pwd;
}

const char *GetHome()
{
    const char *home = getenv("HOME");
    return home == NULL ? NULL : home;
}
const char* DirName(const char* pwd)
{
    if(strcmp("/",pwd) == 0)
    {
        return "/"; 
    }

    int n = strlen(pwd);
    
    const char* ret = nullptr;

    for(int i = n - 1; i >= 0; i--)
    {
        if(pwd[i] == '/')
        {
            ret = pwd + i + 1;
            break;
        }
    }
    return ret;
}

void MakeCommandLine(char cmd_prompt[], int size)
{
    snprintf(cmd_prompt,size,FORMAT,GetUserName(),GetHostName(),DirName(GetPwd()));
}


void PrintCommandPrompt()
{
    char prompt[COMMAND_SIZE];
    MakeCommandLine(prompt,sizeof(prompt));
    printf("%s",prompt);
    fflush(stdout);
}

bool GetCommandLine(char* out, int size)
{

    char *c = fgets(out,size,stdin);
    if(c == NULL)
    {
        return false;
    }
    out[strlen(out)-1] = 0;//清理\n
    if(strlen(out) == 0) return false;
    return true;
}

bool CommandParse(char *commandline)
{
    
    //命令行分析 "ls -a -l" -> "ls" "-a" "-l"
    //切割
#define SEP " "
    g_argc = 0;

    g_argv[g_argc++] = strtok(commandline,SEP);

    while((bool)(g_argv[g_argc++] = strtok(nullptr,SEP)));
    g_argc--;
    return g_argc > 0 ? true:false;
}

void PrintArgv()
{
    for(int i = 0; g_argv[i]; i++)
    {
        printf("g_argv[%d]:%s\n",i,g_argv[i]);
    }
    printf("argc:%d\n",g_argc);
}
bool Cd()
{
    
    if(g_argc == 1)
    {
        const char* home = GetHome();
        if(home == NULL) return true;
        chdir(home);
    }
    else 
    {
        std::string where = g_argv[1];
        if(where == "-")
        {
            // 回到家目录
        }
        else if(where == "~")
        {
            // 回到上一次目录
        }
        else 
        {
            chdir(where.c_str());
        }
    }
    return true;
}

bool Echo()
{
    if(g_argc == 2)
    {
        // echo "hello world"
        // echo $?
        // echo $PATH
        std::string opt = g_argv[1];
        if(opt == "$?")
        {
            printf("%d\n",lastcode);
            lastcode = 0;
            return true;
        }
        else if(opt[0] == '$')
        {
            std::string env_name = opt.substr(1);
            const char *env_value = getenv(env_name.c_str());
            if(env_value)
            {
                std::cout << env_value <<std::endl;
                lastcode = 0;
                return true;
            }
        }
    }
    return false;
}

bool CheckAndExecBuiltin()
{
    std::string cmd = g_argv[0];
    if(strcmp(g_argv[0],"cd") == 0)
    {
        Cd();
        return true;
    }
    else if(cmd == "echo")
    {
        if(Echo())
            return true;
    }
    else if(cmd == "export")
    {

    }
    else if(cmd == "alias")
    {

    }
    return false;
}

#define NO_REDIR 0
#define INPUT_REDIR 1
#define OUT_REDIR 2
#define ADD_REDIR 3
#include <ctype.h>
#define Clear_Space(pos)do\
{\
    while(isspace(*pos))\
        pos++;\
}while(0);
int REDIR = 0;
char *FileName = nullptr;
void  RedirectionCommand(char * com)
{
    REDIR = NO_REDIR;
    FileName = nullptr;
     int end = strlen(com);

     while(end > 0)
     {
         if(com[end] == '<')
         {
             REDIR = INPUT_REDIR;
             com[end++] = '\0';
             FileName = com + end;
            Clear_Space(FileName);
             break;
         }
         else if(com[end] == '>')
         {
            if(com[end-1] == '>')
            {

                REDIR = ADD_REDIR;
                com[end-1] = '\0';
                com[end++] = '\0';
            }
            else 
            {
                REDIR = OUT_REDIR;
                com[end++] = '\0';
            }
            FileName = com + end;
            Clear_Space(FileName);
            break;
         }
         end--;
     }
}

int Execute()
{

    pid_t id = fork();
    if(id == 0)
    {
        if(REDIR == INPUT_REDIR)
        {
            int fd = open(FileName,O_RDONLY);
            if(fd < 0) exit(1);
            dup2(fd,0);
            close(fd);
        }
        else if(REDIR == ADD_REDIR)
        {
            int fd = open(FileName,O_CREAT | O_APPEND,0666);
            if(fd < 0) exit(1);
            dup2(fd,1);
            close(fd);

        }
        else if(REDIR == OUT_REDIR)
        {

            int fd = open(FileName,O_CREAT | O_WRONLY | O_TRUNC,0666);
            if(fd < 0) exit(1);
            dup2(fd,1);
            close(fd);
        }
        //child
       execvp(g_argv[0],g_argv); 
        exit(0);
    }

    int status;
    // father
    pid_t rid = waitpid(id,&status,0);
    if(rid > 0)
    {
        lastcode = WEXITSTATUS(status);
    }
    return 0;
}

void InitEnv()
{
    extern char **environ;
    memset(g_env,0,sizeof(g_env));
    g_envs = 0;

    // 本来是要从配置文件来
    for(int i = 0; environ[i]; i++)
    {
        // 1.1 申请空间
        g_env[i] = (char*)malloc(strlen(environ[i])+1);
        if(g_env[i] == NULL)
        {
            exit(1);
        }
        strcpy(g_env[i],environ[i]);
        g_envs++;
    }
    g_env[g_envs] = NULL;

    // 2.导成环境变量
    for(int i = 0; g_env[i]; i++)
    {
        putenv(g_env[i]);
    }
}


void cleannup()
{
    for(int i = 0; i < g_envs; i++)
    {
        free(g_env[i]);
        g_env[i]=NULL;
    }
}

int main()
{
    // shell 启动的时候，从系统中获取环境变量
    // 我们的环境变量信息应该从父shell统一来
    InitEnv();

    while(true)
    {

        // 1. 输出命令行提示符
        //printf("[%s@%s %s]# ",GetUserName(),GetHostName(),GetPwd());
        PrintCommandPrompt();
    
        // 2. 获取用户输入的命令
        char commandline[COMMAND_SIZE];
        if(!GetCommandLine(commandline,sizeof(commandline)))
        {
            continue;
        }
        //printf("echo %s\n",commandline);
        
        // 3. 重定向命令分析 > >> <
        RedirectionCommand(commandline);
        
        // 4. 命令行分析 "ls -a -l" -> "ls" "-a" "-l"
        if(!CommandParse(commandline))
            continue;
        //PrintArgv();
       

        // 检查别名
        // 5.检查并处理内键命令
        if(CheckAndExecBuiltin())
            continue;


        // 6.执行命令
        Execute();

    }
    
    //清理内存空间
    cleannup();
    return 0;
}
